#!/usr/bin/env python
# -*- coding: utf-8 -*-


import zmq
import os
import sys
import argparse
import logging
    
from api import API


class ProxyServer:
    def __init__(
        self,
        host: str,
        rep_port: int,
        pub_port: int,
        http_host: str,
        http_port: int
    ):
        self.api = API(http_host, http_port)
        self.context = zmq.Context()
        self.rep_socket = self.context.socket(zmq.REP)
        self.pub_socket = self.context.socket(zmq.PUB)
        self.rep_socket.bind(f"tcp://{host}:{rep_port}")
        self.pub_socket.bind(f"tcp://{host}:{pub_port}")

    def __del__(self):
        self.close()

    def close(self):
        if self.rep_socket:
            self.rep_socket.close()
            self.rep_socket = None
        if self.pub_socket:
            self.pub_socket.close()
            self.pub_socket = None
        if self.context:
            self.context.term()
            self.context = None

    def send_json(self, response_data):
        try:
            self.rep_socket.send_json(response_data)
            return None
        except zmq.error.ZMQError as e:
            logging.error(f"发送JSON数据时发生ZMQ错误: {e}")
            return str(e)
        except TypeError as e:
            logging.error(f"发送JSON数据时发生类型错误: {e}")
            return str(e)
        except Exception as e:
            logging.error(f"发送JSON数据时发生未知错误: {e}")
            return str(e)

    def recv_json(self):
        try:
            request = self.rep_socket.recv_json()
            return request
        except zmq.error.ZMQError as e:
            logging.error(f"发送JSON数据时发生ZMQ错误: {e}")
            return None

    def run(self):
        while True:
            request = self.recv_json()
            logging.debug(f"Received request: {request}")
            response = self.api.call(request)
            logging.debug(f"Sending response: {response}")
            errmsg = self.send_json(response)
            if errmsg is not None:
                logging.error(f"发送响应时发生错误: {errmsg}")
                break # or reconnect to the client

def daemonize():
    # Daemonize the process
    try:
        pid = os.fork()
        if pid > 0:
            # exit first parent
            sys.exit(0)
    except OSError as e:
        sys.stderr.write(f"fork #1 failed: {e.errno} ({e.strerror})\n")
        sys.exit(1)

    os.chdir("/")
    os.setsid()
    os.umask(0)

    try:
        pid = os.fork()
        if pid > 0:
            # exit second parent
            sys.exit(0)
    except OSError as e:
        sys.stderr.write(f"fork #2 failed: {e.errno} ({e.strerror})\n")
        sys.exit(1)

    # redirect standard file descriptors
    sys.stdout.flush()
    sys.stderr.flush()
    # It's good practice to use a log file for daemons, but for simplicity, we'll use /dev/null
    # For actual logging, replace os.devnull with a log file path
    # For example: log_file = "/tmp/daemon_proxy.log"
    # so = open(log_file, 'a+')
    # se = open(log_file, 'a+')
    si = open(os.devnull, 'r')
    so = open(os.devnull, 'a+')
    se = open(os.devnull, 'a+')

    os.dup2(si.fileno(), sys.stdin.fileno())
    os.dup2(so.fileno(), sys.stdout.fileno())
    os.dup2(se.fileno(), sys.stderr.fileno())


def main():
    parser = argparse.ArgumentParser(description="ZMQ Proxy Server")
    parser.add_argument('--daemon', action=argparse.BooleanOptionalAction, default=True, help='Run as daemon (default: True)')
    parser.add_argument('--proxy-host', type=str, default='0.0.0.0', help='IP address to bind (default: 0.0.0.0)')
    parser.add_argument('--proxy-port', type=int, default=5555, help='port to bind (default: 5555)')
    parser.add_argument('--http-host', type=str, required=True, help='HTTP server IP address')
    parser.add_argument('--http-port', type=int, required=True, help='HTTP port')
    parser.add_argument('--socket-host', type=str, default='0.0.0.0', help='Socket server IP address (default: 0.0.0.0)')
    parser.add_argument('--socket-port', type=int, default=5000, help='Socket server port (default: 5000)')
    parser.add_argument('--monitor-interval', type=int, default=30, help='Device status monitoring interval in seconds (default: 30)')
    parser.add_argument('--log-file', type=str, default='proxy_server.log', help='Log file path (default: proxy_server.log)')
    parser.add_argument('--log-level', type=str, default='INFO', help='Log level: DEBUG, INFO, WARNING, ERROR, CRITICAL (default: INFO)')
    parser.add_argument('--images-dir', type=str, default='./images', help='Images directory path (default: ./images)')
    args = parser.parse_args()

    # 解析日志级别
    match args.log_level:
        case "DEBUG":
            logging_level = logging.DEBUG
        case "INFO":
            logging_level = logging.INFO
        case "WARNING":
            logging_level = logging.WARNING
        case "ERROR":
            logging_level = logging.ERROR
        case "CRITICAL":
            logging_level = logging.CRITICAL
        case _:
            logging_level = logging.INFO
    
    # 配置日志
    if args.daemon:
        daemonize()
        logging.basicConfig(
            level=logging_level,
            filename=args.log_file,    # 指定日志文件
            filemode='w',          # 文件写入模式，'w'覆盖，'a'追加
            format='%(asctime)s - %(levelname)s - %(message)s',
            datefmt='%Y-%m-%d %H:%M:%S')
    else:
        logging.basicConfig(
            level=logging_level,
            format='%(asctime)s - %(levelname)s - %(message)s',
            datefmt='%Y-%m-%d %H:%M:%S')
        
    # 设置环境变量，用于图片存储
    os.environ['IMAGES_DIR'] = os.path.abspath(args.images_dir)
    
    # 输出服务器信息
    logging.info(f"Proxy Server will listen on: {args.proxy_host}:{args.proxy_port}")
    logging.info(f"Log file: {os.path.abspath(args.log_file)}")
    logging.info(f"Log level: {args.log_level}")
    logging.info(f"Images directory: {os.environ['IMAGES_DIR']}")

    proxy_server = ProxyServer(
        host=args.proxy_host,
        rep_port=args.proxy_port,
        pub_port=args.proxy_port+1,
        http_host=args.http_host,
        http_port=args.http_port,
        timeout=5000
    )
    try:
        proxy_server.init_hypium_drivers()
        proxy_server.run()
    finally:
        proxy_server.close()

if __name__ == "__main__":
    main()